package org.bleep;

import org.bleep.input.*;
import org.bleep.util.*;

import org.lwjgl.opengl.glu.*;
import org.lwjgl.opengl.*;
import org.lwjgl.Sys;
import org.lwjgl.BufferUtils;

import java.nio.ByteBuffer;
import java.util.TimerTask;

/**
* <<Class summary>>
*
* @author   &lt;John Cromartie&gt;
* @version $Rev$
*/
public abstract class Game
{
	private String gameTitle = "New *bleep* Game";
	private String version = "0.0.1";
	private boolean fullScreen = false;

	private boolean finished = false;

	protected float gameWidth = 320f;
	protected float gameHeight = 240f;
	private float displayWidth = 640f;
	private float displayHeight = 480f;
	
	private static final float SCREEN_TEX_SIZE = 512f;

	private long fps = 60;

	private float interpolation;
	private long interval;

	private int screenTexture;

	public Game()
	{
		sysInit();
		initGL();
		init();
	}

	/**
	* init
	*/
	private boolean sysInit()
	{
		try
		{
			Display.setTitle(gameTitle + ": " + version);
			Display.setFullscreen(fullScreen);
			Display.setVSyncEnabled(true);
			Display.setDisplayMode(new DisplayMode((int)displayWidth, (int)displayHeight));
			Display.create();
			// Scene.init();
			initGL();

			InputSystem.init();
			
			interval = 1000 / fps;
			interpolation = (float)interval / 1000f;

			setupScreenTexture();

			return true;
		}
		catch (Exception e)
		{
			System.out.println("Error initializing display.");
			e.printStackTrace();
			return false;
		}
	}
	
	private void setupScreenTexture()
	{
		GL11.glEnable(GL11.GL_TEXTURE_2D);
		
		screenTexture = TextureManager.createTextureID();
		ByteBuffer colorBits = BufferUtils.createByteBuffer((int)SCREEN_TEX_SIZE * (int)SCREEN_TEX_SIZE * 3);
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, screenTexture);
		GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_DECAL);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT); 
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); 
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, (int)SCREEN_TEX_SIZE, (int)SCREEN_TEX_SIZE, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, colorBits);
		
		GL11.glDisable(GL11.GL_TEXTURE_2D);
	}

	/**
	* initGL
	*/
	private void initGL()
	{
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();

		// positive-y = up
		GL11.glOrtho(0f, displayWidth, 0f, displayHeight, -2f, 2f);

		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();

		GL11.glShadeModel(GL11.GL_FLAT);
		GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		// GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);

		// GL11.glTexEnvf( GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE );

		// GL11.glBlendFunc(GL11.GL_SRC_ALPHA,GL11.GL_ONE_MINUS_SRC_ALPHA);
		// GL11.glEnable(GL11.GL_BLEND);

		GL11.glEnable(GL11.GL_DEPTH_TEST);
		GL11.glEnable(GL11.GL_TEXTURE_2D);
	}


	/**
	* method
	*/
	public void run()
	{
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
		while(!finished)
		{
			if (Display.isCloseRequested())
			{
				finished = true;
			}
			else if (Display.isActive())
			{
				InputSystem.update();
				update(interpolation);
				preRender();
				render();
				postRender();
				Display.update();
				try
				{
					Thread.currentThread().sleep(interval);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}
	}

	protected void quit()
	{
		finished = true;
	}

	private void renderToTexture(int texture)
	{
		GL11.glColor4f(0f, 0f, 0f, 1f);
		GL11.glReadBuffer(GL11.GL_BACK);
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture);
		GL11.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, 0, 0, (int)SCREEN_TEX_SIZE, (int)SCREEN_TEX_SIZE, 0);
	}

	private void preRender()
	{
		GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_COLOR_BUFFER_BIT);
		GL11.glLoadIdentity();
	}

	private void postRender()
	{
		renderToTexture(screenTexture);

		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
		
		GL11.glBegin(GL11.GL_QUADS);
		{
			GL11.glTexCoord2f(0f, 0f);
			GL11.glVertex2f(0f, 0f);
			
			GL11.glTexCoord2f(gameWidth / SCREEN_TEX_SIZE, 0f);
			GL11.glVertex2f(displayWidth, 0f);
			
			GL11.glTexCoord2f(gameWidth / SCREEN_TEX_SIZE, gameHeight / SCREEN_TEX_SIZE);
			GL11.glVertex2f(displayWidth, displayHeight);
			
			GL11.glTexCoord2f(0f, gameHeight / SCREEN_TEX_SIZE);
			GL11.glVertex2f(0f, displayHeight);
		}
		GL11.glEnd();
	}

	public abstract void init();
	public abstract void render();
	public abstract void update(float timeStep);
}